﻿using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;

namespace PmSoft.Core.Reflection;

/// <summary>
/// 类型反射服务
/// </summary>
/// <typeparam name="T">类型</typeparam>
public class ReflectService<T>
{
	private readonly ReflectionContext _reflectionContext;
	private readonly object? _instance;

	/// <summary>
	/// 初始化 ReflectService 类的新实例
	/// </summary>
	/// <param name="ctx">反射上下文</param>
	/// <param name="instance">对象实例</param>
	internal ReflectService(ReflectionContext ctx, object? instance)
	{
		_instance = instance;
		_reflectionContext = ctx;
	}

	/// <summary>
	/// 获取反射上下文
	/// </summary>
	public ReflectionContext Context => _reflectionContext;

	/// <summary>
	/// 获取指定参数类型的构造函数
	/// </summary>
	/// <param name="types">参数类型数组</param>
	/// <returns>构造函数信息</returns>
	public ConstructorExplorer? GetConstructor(params Type[] types)
	{
		var ctors = Context.Constructors.Where(x => x.Parameters.Length == types.Length);
		foreach (var ctor in ctors)
		{
			var f = true;
			for (var i = 0; i < types.Length; i++)
			{
				if (ctor.Parameters[i].ParameterType != types[i])
				{
					f = false;
					break;
				}
			}
			if (f) return ctor;
		}
		return null;
	}

	/// <summary>
	/// 获取指定名称的属性信息
	/// </summary>
	/// <param name="name">属性名称</param>
	/// <returns>属性信息</returns>
	public PropertyInfo? GetProperty(string name) => _reflectionContext.Properties.FirstOrDefault(x => x.Member.Name == name)?.Member;

	/// <summary>
	/// 获取指定名称的字段信息
	/// </summary>
	/// <param name="name">字段名称</param>
	/// <returns>字段信息</returns>
	public FieldInfo? GetField(string name) => _reflectionContext.Fields.FirstOrDefault(x => x.Member.Name == name)?.Member;

	/// <summary>
	/// 扫描程序集中的类型
	/// </summary>
	/// <typeparam name="TTarget">目标类型</typeparam>
	/// <returns>符合条件的类型集合</returns>
	public IEnumerable<Type> ScanTypes<TTarget>()
	{
		return _reflectionContext.Assembly.ExportedTypes.Where(x => typeof(TTarget).IsAssignableFrom(x) && !x.IsAbstract);
	}

	/// <summary>
	/// 复制对象
	/// </summary>
	/// <typeparam name="TTarget">目标类型</typeparam>
	/// <returns>复制后的对象</returns>
	public TTarget Copy<TTarget>() where TTarget : new()
	{
		if (_instance == null) return new();
		var instance = TypeFactory<TTarget>.Instance();
		return Copy(instance);
	}

	/// <summary>
	/// 复制对象到目标对象
	/// </summary>
	/// <typeparam name="TTarget">目标类型</typeparam>
	/// <param name="target">目标对象</param>
	/// <returns>复制后的目标对象</returns>
	public TTarget Copy<TTarget>(TTarget target)
	{
		var func = InternalCopy(target);
		func((T)_instance, target);
		return target;
	}

	/// <summary>
	/// 内部复制方法
	/// </summary>
	/// <typeparam name="TTarget">目标类型</typeparam>
	/// <param name="target">目标对象</param>
	/// <returns>复制操作的委托</returns>
	private Action<T, TTarget> InternalCopy<TTarget>(TTarget target)
	{
		var targetType = target.GetType();
		ReflectionContext targetCtx;
		if (targetType != _reflectionContext.Type)
		{
			targetCtx = ReflectionContextCache.GetOrAdd(targetType);
		}
		else
		{
			targetCtx = _reflectionContext;
		}

		return new ObjectCopier<T, TTarget>(_reflectionContext, targetCtx).Copy();
	}

	/// <summary>
	/// 对象复制器
	/// </summary>
	/// <typeparam name="TSource">源类型</typeparam>
	/// <typeparam name="TTarget">目标类型</typeparam>
	private sealed class ObjectCopier<TSource, TTarget>
	{
		private static readonly ConcurrentDictionary<TypeKey, WeakReference<Delegate>> _delegateCache = new();

		private readonly ReflectionContext _source;
		private readonly ReflectionContext _target;

		/// <summary>
		/// 初始化 ObjectCopier 类的新实例
		/// </summary>
		/// <param name="source">源反射上下文</param>
		/// <param name="target">目标反射上下文</param>
		public ObjectCopier(ReflectionContext source, ReflectionContext target)
		{
			_source = source;
			_target = target;
		}

		/// <summary>
		/// 复制对象
		/// </summary>
		/// <returns>复制操作的委托</returns>
		public Action<TSource, TTarget> Copy()
		{
			var key = new TypeKey(_source.Type, _target.Type);
			if (_delegateCache.TryGetValue(key, out var weakRef) && weakRef.TryGetTarget(out var cachedDelegate))
			{
				return (Action<TSource, TTarget>)cachedDelegate;
			}

			var newDelegate = GenerateDelegate(_source, _target);
			_delegateCache[key] = new WeakReference<Delegate>(newDelegate);
			return (Action<TSource, TTarget>)newDelegate;
		}

		/// <summary>
		/// 生成复制操作的委托
		/// </summary>
		/// <param name="from">源反射上下文</param>
		/// <param name="to">目标反射上下文</param>
		/// <returns>复制操作的委托</returns>
		private Delegate GenerateDelegate(ReflectionContext from, ReflectionContext to)
		{
			var source = Expression.Parameter(from.Type, "source");
			var target = Expression.Parameter(to.Type, "target");

			var blocks = new List<Expression>();
			foreach (var member in to.Properties.Where(x => x.Member.CanWrite))
			{
				var getter = PropertyOrFieldFn(source, member, from.Properties);

				if (getter != null)
				{
					blocks.Add(member.ExpressionModel.SetExpression(target, getter));
				}
			}

			var lambda = Expression.Lambda<Action<TSource, TTarget>>(Expression.Block(blocks), source, target);
			return lambda.Compile();
		}

		/// <summary>
		/// 获取属性或字段的表达式
		/// </summary>
		/// <param name="source">源表达式</param>
		/// <param name="destinationMember">目标成员信息</param>
		/// <param name="members">成员信息集合</param>
		/// <returns>属性或字段的表达式</returns>
		private Expression? PropertyOrFieldFn(Expression source, PropertyInfoExplorer destinationMember, IEnumerable<PropertyInfoExplorer> members)
		{
			return members.Where(member => member.Member == destinationMember.Member)
				.Select(member => member.ExpressionModel.GetExpression(source))
				.FirstOrDefault();
		}

		/// <summary>
		/// 类型键，用于缓存委托
		/// </summary>
		private readonly struct TypeKey : IEquatable<TypeKey>
		{
			public bool Equals(TypeKey other)
			{
				return Source == other.Source && Destination == other.Destination;
			}

			public override bool Equals(object obj)
			{
				if (obj is not TypeKey)
					return false;
				return Equals((TypeKey)obj);
			}

			public override int GetHashCode()
			{
				return (Source.GetHashCode() << 16) ^ (Destination.GetHashCode() & 65535);
			}

			public static bool operator ==(TypeKey left, TypeKey right)
			{
				return left.Equals(right);
			}

			public static bool operator !=(TypeKey left, TypeKey right)
			{
				return !left.Equals(right);
			}

			public Type Source { get; }
			public Type Destination { get; }

			public TypeKey(Type source, Type destination)
			{
				Source = source;
				Destination = destination;
			}
		}
	}
}

